﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using UnityEngine;

namespace Framework.Network
{

    public class NetworkChannel : INetworkChannel
    {
        private Socket m_Socket;
        private INetworkChannelHelper m_helper;

        public ChannelEvent Event => m_channelEvent;
        private ChannelEvent m_channelEvent;
        private float timer = 0;
        public void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            if (m_Socket != null && m_Socket.Connected &&Event.HeartbeatTime != 0)
            {
                timer += realElapseSeconds;
                if (timer > Event.HeartbeatTime)
                {
                    timer -= Event.HeartbeatTime;
                    Send(Event.OnHeartbeat(Event.content));
                }
            }

        }

        public string Name { get; private set; }
        public INetworkChannelHelper Helper => m_helper;

        private ReceiveStreamState m_receiveStreamState;
        private StreamState m_sendStreamState;


        public NetworkChannel(string name, INetworkChannelHelper helper)
        {
            
            Name = name;
            m_helper = helper;
            m_receiveStreamState = new ReceiveStreamState();
            m_sendStreamState = new StreamState();
            m_helper.Init();
            m_channelEvent = new ChannelEvent();
        }

        public void Close()
        {
            lock (this)
            {
                if (m_Socket == null)
                {
                    return;
                }
                
                try
                {
                    m_Socket.Shutdown(SocketShutdown.Both);
                }
                catch
                {
                }
                finally
                {
                    m_Socket.Close();
                    m_Socket = null;
                    Event.OnClose?.Invoke(Event.content);
                }
            }
        }

        public void ShutDown()
        {
            Close();
            m_channelEvent.Clear();

        }

        public bool Connected
        {
            get
            {
                if (m_Socket == null)
                {
                    return false;
                }

                return m_Socket.Connected;
            }
        }
        // public void Send(byte[] info)
        // {
        //     throw new NotImplementedException();
        // }

        private Queue<byte[]> m_sendPool = new Queue<byte[]>();
        public void Send(byte[] info)
        {
            m_sendPool.Enqueue(info);
            SendAsync();
        }

        void SendAsync()
        {
            if(m_Socket==null) return;
            if (m_sendPool.Count > 0)
            {
                var info = m_sendPool.Dequeue();
                m_sendStreamState.Stream.Write(info);
                m_Socket.BeginSend(m_sendStreamState.Stream.GetBuffer(), 0, (int)
                    (m_sendStreamState.Stream.Position),
                    SocketFlags.None, SendCallback,m_Socket);
            }
           
        }



        public void Connect(string serverIp, int serverPort)
        {
            if (m_Socket != null)
            {
                Close();
                m_Socket = null;
            }
            m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
            m_sendStreamState.Reset();
            
            m_receiveStreamState.PrepareForPacketHeader(m_helper.HeaderLength);
            try
            {
                m_Socket.BeginConnect(serverIp,serverPort, ConnectCallback,m_Socket);
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
                Event.OnError?.Invoke(Event.content,e.Message);
                throw;
            }
            
        }

        void ReceiveAsync()
        {
            // Log.Info("开始接受数据");
            if(m_Socket==null) return;
            try
            {
                m_Socket.BeginReceive(m_receiveStreamState.Stream.GetBuffer(), (int)m_receiveStreamState.Stream.Position,
                    (int)(m_receiveStreamState.Stream.Length - m_receiveStreamState.Stream.Position), SocketFlags.None,ReceiveCallback,
                    m_Socket);
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
                Event.OnError?.Invoke(Event.content,e.Message);
               throw;
            }
           
        }
        void ConnectCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            try
            {
                socket.EndConnect(ar);
            }
            catch (Exception  e)
            {
                Log.Error(e.Message);
                Event.OnError?.Invoke(Event.content,e.Message);
                return;
            }
            Event.OnConnect?.Invoke(Event.content);
            ReceiveAsync();
        }
        void SendCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            if (!socket.Connected)
            {
                return;
            }
            try
            {
                socket.EndSend(ar);
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
                Event.OnError?.Invoke(Event.content,e.Message);
                return;
            }
            
            if (m_sendStreamState.Stream.Position < m_sendStreamState.Stream.Length)
            {
                SendAsync();
                return;
            }
            m_sendStreamState.Reset();
            if (m_sendPool.Count > 0)
            {
                SendAsync();
            }
        }

        void ReceiveCallback(IAsyncResult ar)
        {

            Socket socket = (Socket)ar.AsyncState;
            if (!socket.Connected)
            {
                return;
            }

            int bytesReceived = 0;
            
            try
            {
                bytesReceived= socket.EndReceive(ar);
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
                Event.OnError?.Invoke(Event.content,e.Message);;
            }
            if (bytesReceived <= 0)
            {
                Log.Error("远端断开");
                Event.OnError?.Invoke(Event.content,"远端断开");
                Close();
                return;
            }

            m_receiveStreamState.Stream.Position += bytesReceived;
            if (m_receiveStreamState.Stream.Position < m_receiveStreamState.Stream.Length)
            {
                ReceiveAsync();
                return;
            }

            bool processSuccess = false;
            if (m_receiveStreamState.isOnReceiveHeader)
            {
                //解析包头
                processSuccess = ProcessPacketHeader();
            }
            else
            {
                //解析包体
                processSuccess = ProcessPacket();
            }
            if (processSuccess)
            {
                ReceiveAsync();
                return;
            }
        }
        bool ProcessPacket()
        {
            byte[] bytes = m_helper.DeserializePacket(m_receiveStreamState.Stream);
            // Event.OnMessage?.Invoke(Event.content,System.Text.Encoding.Default.GetString(bytes));
            Event.OnMessage?.Invoke(Event.content,bytes);
            m_receiveStreamState.PrepareForPacketHeader(m_helper.HeaderLength);
            return true;
        }
        private bool ProcessPacketHeader()
        {
            long length = m_helper.DeserializePacketHeader(m_receiveStreamState.Stream);
             m_receiveStreamState.PrepareForPacket(length);
             return true;
        }
    }

}